package org.finesys.common.dataPermission.dept.rule;

import cn.hutool.core.lang.Opt;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.metadata.TableInfoHelper;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import net.sf.jsqlparser.expression.Alias;
import net.sf.jsqlparser.expression.Expression;
import net.sf.jsqlparser.expression.LongValue;
import net.sf.jsqlparser.expression.NullValue;
import net.sf.jsqlparser.expression.operators.conditional.OrExpression;
import net.sf.jsqlparser.expression.operators.relational.EqualsTo;
import net.sf.jsqlparser.expression.operators.relational.ExpressionList;
import net.sf.jsqlparser.expression.operators.relational.InExpression;
import org.finesys.common.core.module.DeptDataPermissionEntity;
import org.finesys.common.dataPermission.dept.service.DataPermissionService;
import org.finesys.common.dataPermission.rule.DataPermissionRule;
import org.finesys.common.mp.core.utils.MyBatisUtils;
import org.finesys.common.security.core.module.AuthUser;
import org.finesys.common.security.core.util.SecurityUtils;
import org.springframework.util.StringUtils;

import java.util.*;
import java.util.stream.Collectors;

@Slf4j
@AllArgsConstructor
public class DeptDataPermissionRule implements DataPermissionRule {
    /**
     * 部门条件字段
     */
    private static final String DEPT_COLUMN_NAME = "dept_id";
    /**
     * 用户条件字段
     */
    private static final String USER_COLUMN_NAME = "user_id";

    /**
     * 空表达式
     */
    private static final Expression EXPRESSION_NULL = new NullValue();
    /**
     * 基于部门的字段配置 一般情况下 每个表的部门编号字段是 dept_id 通过该配置自定义
     * <p>
     * 表名:条件字段名
     */
    private final Map<String, String> deptColumns = new HashMap<>();
    /**
     * 基于用户的字段配置 一般情况下 每个表的用户编号字段是 user_id 通过该配置自定义
     * <p>
     * 表名:条件字段名
     */
    private final Map<String, String> userColumns = new HashMap<>();

    /**
     * 所有表名 是 {@link #deptColumns} 和 {@link #userColumns} 的合集
     */
    private final Set<String> tableNames = new HashSet<>();

    private final DataPermissionService dataPermissionService;

    @Override
    public Set<String> getTableNames() {
        return tableNames;
    }

    @Override
    public Expression getExpression(String tableName, Alias tableAlias) {
        //当前登录用户信息
        AuthUser authUser = SecurityUtils.getUser();
        // 只有有登陆用户的情况下 才进行数据权限的处理
        if (Objects.isNull(authUser)) {
            return null;
        }
        // 获得用户的数据权限
        DeptDataPermissionEntity deptDataPermissionEntity = Opt.of(dataPermissionService.getDeptDataPermission(authUser))
                .get();
        //查看全部
        if (deptDataPermissionEntity.getAll()) {
            return null;
        }
        // 不能查看部门 又不能查看自己 则无权限
        if (ArrayUtil.isEmpty(deptDataPermissionEntity.getDeptIds()) && Boolean.FALSE.equals(deptDataPermissionEntity.getSelf())) {
            //where null=null 返回空的数据
            return new EqualsTo(null, null);
        }
        //构建部门查询条件
        Expression deptExpression = this.buildDeptExpression(tableName, tableAlias, deptDataPermissionEntity.getDeptIds());
        //构建用户查询条件
        Expression userExpression = this.buildUserExpression(tableName, tableAlias, deptDataPermissionEntity.getSelf(), authUser.getUserId());
        if (Objects.isNull(deptExpression) && Objects.isNull(userExpression)) {
            if (log.isWarnEnabled()) {
                log.warn("LoginUser{} Table{}/{} DeptDataPermission {}构建的条件为空", authUser.getUsername(), tableName, tableAlias, deptDataPermissionEntity);
            }
            return EXPRESSION_NULL;
        }
        if (Objects.isNull(deptExpression)) {
            return userExpression;
        }
        if (Objects.isNull(userExpression)) {
            return deptExpression;
        }
        return new OrExpression(deptExpression, userExpression);
    }

    /**
     * 构建部门查询的条件
     *
     * @param tableName  表名
     * @param tableAlias 表别名
     * @param deptIds    允许查看的部门id
     * @return 查询条件 where dept_id in ()
     */

    private Expression buildDeptExpression(String tableName, Alias tableAlias, Set<Long> deptIds) {
        // 如果不存在这张表的部门条件配置 则无需作为条件
        String columnName = deptColumns.get(tableName);
        if (!StringUtils.hasText(columnName)) {
            return null;
        }
        // 如果允许查看的部门为空 则不拼接部门查询条件
        if (ArrayUtil.isEmpty(deptIds)) {
            return null;
        }
        //拼接条件
        return new InExpression(MyBatisUtils.bulidColumn(tableAlias, columnName), new ExpressionList(deptIds.stream().map(LongValue::new).collect(Collectors.toList())));
    }

    /**
     * 构建用户查询的条件
     *
     * @param tableName  表名
     * @param tableAlias 表别名
     * @param self       是否只允许自己查看
     * @param userId     用户id
     * @return 查询条件 where user_id = ''
     */
    private Expression buildUserExpression(String tableName, Alias tableAlias, Boolean self, Long userId) {
        // 如果不查看自己 则无需作为条件
        if (Boolean.FALSE.equals(self)) {
            return null;
        }
        // 获取这张表中作为查询条件的列名
        String columnName = userColumns.get(tableName);
        if (StrUtil.isEmpty(columnName)) {
            return null;
        }
        //拼接条件
        return new EqualsTo(MyBatisUtils.bulidColumn(tableAlias, columnName), new LongValue(userId));
    }

    /**
     * entityClass对应的表 以DEPT_COLUMN_NAME为查询条件
     */
    public void addDeptColumn(Class<? extends Object> entityClass) {
        addDeptColumn(entityClass, DEPT_COLUMN_NAME);
    }

    /**
     * entityClass对应的表 以columnName列为查询条件
     */
    public void addDeptColumn(Class<? extends Object> entityClass, String columnName) {
        //获取对应实体类的表名
        String tableName = TableInfoHelper.getTableInfo(entityClass).getTableName();
        addDeptColumn(tableName, columnName);
    }

    /**
     * 储存使用此规则的表名、列名
     */
    public void addDeptColumn(String tableName, String columnName) {
        deptColumns.put(tableName, columnName);
        tableNames.add(tableName);
    }

    /**
     * entityClass对应的实体类以USER_COLUMN_NAME列来查询
     */
    public void addUserColumn(Class<? extends Object> entityClass) {
        addUserColumn(entityClass, USER_COLUMN_NAME);
    }

    /**
     * entityClass对应的表 以columnName列为查询条件
     */
    public void addUserColumn(Class<? extends Object> entityClass, String columnName) {
        // 获取实体类对应的表名
        String tableName = TableInfoHelper.getTableInfo(entityClass).getTableName();
        addUserColumn(tableName, columnName);
    }

    /**
     * 储存使用此规则的表名、列名
     */
    public void addUserColumn(String tableName, String columnName) {
        userColumns.put(tableName, columnName);
        tableNames.add(tableName);
    }
}
